package tx.os3.clientserver.client.gui;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.Observable;
import java.util.Observer;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import tx.os3.clientserver.client.gui.FileDrop.Listener;
import tx.os3.clientserver.client.handlers.UICommandHandler;
import tx.os3.clientserver.client.model.FileListModel;
import tx.os3.clientserver.client.model.ServerModel;
import tx.os3.clientserver.client.model.ServerModel.ServerStatus;

/**
 * Client application main window.
 * 
 * @author Thomas Middeldorp
 */
@SuppressWarnings("serial")
public class MainWindow extends JFrame implements Observer {

    private static final String CONNECT_COMMAND_TEXT = "Connect";
    private static final String DISCONNECT_COMMAND_TEXT = "Disconnect";
    private static final String SERVER_INFO_COMMAND_TEXT = "Update server info";

    private Container contentPane;
    private GroupLayout layout;

    private JButton serverInfoCommandButton;
    private JButton connectDisconnectCommandButton;

    private JPanel fileDropPanel;
    private JLabel fileDropLabel;

    private JComponent statusBar;
    private JLabel statusLabel;
    private JLabel serverInfoLabel;

    private FileListComponent fileListComponent;

    private UICommandHandler handler;
    private ServerModel serverModel;

    public MainWindow(UICommandHandler handler, ServerModel serverModel) {
        if(handler == null) {
            throw new IllegalArgumentException(
                    "UICommandHandler cannot be null.");
        }
        if(serverModel == null) {
            throw new IllegalArgumentException("ServerModel cannot be null.");
        }

        // Configure window close event handling
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.addWindowListener(this.createWindowListener());

        // Set layout for content pane
        this.contentPane = this.getContentPane();
        this.contentPane.setBackground(new Color(255, 239, 191));
        this.layout = new GroupLayout(contentPane);

        this.handler = handler;
        this.serverModel = serverModel;

        this.serverInfoCommandButton = new JButton(SERVER_INFO_COMMAND_TEXT);
        this.connectDisconnectCommandButton = new JButton();

        this.fileDropLabel = new JLabel("Drop files here");
        this.fileDropPanel = new JPanel();
        this.initializeFileDropPanel();

        this.fileListComponent = new FileListComponent(
                FileListModel.getInstance(), this.handler);

        // Instantiate status bar components
        this.statusLabel = this.createStatusLabel();
        this.serverInfoLabel = this.createServerInfoLabel();
        this.statusBar = this.createStatusBar();

        this.layoutComponents();
        this.setSize(600, 600);

        this.setCommandHandlers();
        
        this.setTitle("Server tool");
    }

    /**
     * Sets command handlers for all command buttons.
     */
    private void setCommandHandlers() {
        // Set button action listeners
        this.serverInfoCommandButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MainWindow.this.handler.handleInfoCommand();
            }
        });
        this.connectDisconnectCommandButton
                .addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // Set command handler depending on the server status
                        if(MainWindow.this.serverModel.getStatus() == ServerStatus.CONNECTED) {
                            MainWindow.this.handler.handleDisconnectCommand();
                        }
                        else {
                            MainWindow.this.handler.handleConnectCommand();
                        }
                    }
                });
    }

    /**
     * Builds the complete layout of this window
     */
    private void layoutComponents() {
        // Set layout manager
        this.contentPane.setLayout(this.layout);

        // Lay out components
        this.layout.setHorizontalGroup(this.layout
                .createParallelGroup()
                .addGroup(
                        this.layout
                                .createSequentialGroup()
                                .addComponent(this.serverInfoCommandButton)
                                .addComponent(
                                        this.connectDisconnectCommandButton))
                .addComponent(this.fileListComponent, GroupLayout.DEFAULT_SIZE,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(this.fileDropPanel, GroupLayout.PREFERRED_SIZE,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(this.statusBar, GroupLayout.PREFERRED_SIZE,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
        this.layout
                .setVerticalGroup(this.layout
                        .createSequentialGroup()
                        .addGroup(
                                this.layout
                                        .createParallelGroup()
                                        .addComponent(
                                                this.serverInfoCommandButton)
                                        .addComponent(
                                                this.connectDisconnectCommandButton))
                        .addComponent(this.fileListComponent,
                                GroupLayout.DEFAULT_SIZE,
                                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(this.fileDropPanel,
                                GroupLayout.PREFERRED_SIZE,
                                GroupLayout.PREFERRED_SIZE,
                                GroupLayout.PREFERRED_SIZE)
                        .addComponent(this.statusBar,
                                GroupLayout.PREFERRED_SIZE,
                                GroupLayout.PREFERRED_SIZE,
                                GroupLayout.PREFERRED_SIZE));

        this.pack();
    }

    /**
     * Initializes the panel that handles file drops
     */
    private void initializeFileDropPanel() {
        this.fileDropPanel.add(this.fileDropLabel);

        new FileDrop(this.fileDropPanel, new Listener() {

            @Override
            public void filesDropped(File[] files) {
                for(File file : files) {
                    handler.handlePutCommand(file);
                }
                handler.handleDirCommand(null);
            }
        });

        this.fileDropPanel.setPreferredSize(new Dimension(400, 100));
        this.decorateFileDropPanel();
    }

    private void decorateFileDropPanel() {
        this.fileDropLabel.setForeground(Color.GRAY);
        this.fileDropLabel.setFont(new Font("Tahoma", Font.BOLD, 16));

        this.fileDropPanel.setBackground(new Color(255, 246, 219));

    }

    @Override
    public void update(Observable o, Object arg) {
        if(o != null && o.equals(this.serverModel)) {
            this.setStatusBarMessage(this.serverModel.getStatus().toString(),
                    this.serverModel.getServerInfo());
            if(this.serverModel.getStatus() == ServerStatus.CONNECTED) {
                this.connectDisconnectCommandButton
                        .setText(DISCONNECT_COMMAND_TEXT);
            }
            else {
                this.connectDisconnectCommandButton
                        .setText(CONNECT_COMMAND_TEXT);
            }
        }
    }

    /**
     * Creates a window listener for handling window events.
     * 
     * @return The created window listener.
     */
    private WindowListener createWindowListener() {
        return new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                MainWindow.this.handler.handleDisconnectCommand();
            }
        };
    }

    /**
     * Creates a label that represents the status label.
     * 
     * @return The created label
     */
    private JLabel createStatusLabel() {
        return new JLabel();
    }

    /**
     * Creates a label that represents the server info label.
     * 
     * @return The created label
     */
    private JLabel createServerInfoLabel() {
        JLabel label = new JLabel();
        label.setHorizontalTextPosition(SwingConstants.RIGHT);
        label.setHorizontalAlignment(SwingConstants.RIGHT);

        return label;
    }

    /**
     * Creates a status bar component.
     * 
     * @return The created status bar.
     */
    private JComponent createStatusBar() {
        JPanel panel = new JPanel();
        GridLayout layout = new GridLayout(1, 2);
        panel.setLayout(layout);

        panel.setPreferredSize(new Dimension(300, 20));

        panel.add(this.statusLabel);
        panel.add(this.serverInfoLabel);

        return panel;
    }

    private void setStatusBarMessage(String status, String serverInfo) {
        this.statusLabel.setText(status);
        this.serverInfoLabel.setText(serverInfo);
    }
}
